import http from 'http';
import express, { Express } from 'express';

import cors from 'cors';
import cookieParser from 'cookie-parser';
import setupRoutes from './router';
import morgan from 'morgan';
import requestResponseMiddleware from './middleware/requestResponseMiddleware';
import authenticateToken from './middleware/authenticateToken';
import errorHandler from './middleware/errorHandler';
import Log from './utils/log';

class ExpressServer {
  app: Express;
  server: http.Server;

  constructor(public port: number = 9999) {
    //创建一个应用，注意app其实就是一个函数，类似function(req, res) {}
    this.app = express();
    //创建一个http服务器，既然app是一个函数，那这里就可以传入。
    this.server = http.createServer(this.app);
    this.initApp()
      .then(() => {
        this.startServer();
      })
      .catch((err) => {
        Log.error(err);
      });
  }

  async initApp() {
    Log.info('初始化应用');
    this.setupMiddleware();
    await setupRoutes(this.app);
    Log.info('应用初始化完成');
  }

  setupMiddleware() {
    // 日志输出
    this.app.use(morgan('dev'));

    // 设置跨域请求，使用 cors 中间件
    this.app.use(cors());

    // 从 express4.16.0 开始 express已经将部分 body-parser集成到自身的中间件之中,可以直接解析JSON数据
    this.app.use(express.json());

    // 对于前端传输的 formData 数据,需要使用express.urlencoded 解析URL编码的数据,使用 multer处理multipart/form-data中的文件数据
    this.app.use(express.urlencoded({ extended: true }));

    // 请求拦截，统一处理参数，返回结果（注意注册顺序，要在引入路由之前先注册这个拦截器）
    // requestResponseMiddleware 是自定义的中间件，用于统一处理请求和响应
    // authenticateToken 是jwt认证服务中间件，所有的请求都加入登录认证，只有认证通过才能继续访问
    this.app.all('*', requestResponseMiddleware, authenticateToken);

    this.app.use(cookieParser());
    // 引入错误处理中间件
    this.app.use(errorHandler());
  }

  startServer() {
    Log.info('正在启动服务器...');
    this.server.listen(this.port, '0.0.0.0', () => {
      Log.info(`服务已启动,运行端口: http://localhost:${this.port}`);
    });
  }

  close() {
    if (this.server !== undefined) {
      this.server.close();
      Log.info(`服务已停止运行, 端口: http://localhost:${this.port}`);
    }
  }
}

export default ExpressServer;

// 重写 Express 的网络请求
declare global {
  // eslint-disable-next-line @typescript-eslint/no-namespace
  namespace Express {
    // 扩展Request类型
    export interface Request {
      /** 获取所有的请求参数，包含任何请求，并重组为对象 返回JSON对象 */
      getParams(): { [key: string]: unknown };
      /** 判断是移动设备还是PC设备 */
      isMobile: boolean;
      /** 用户id，只有在登录token认证通过后，再赋值此字段 */
      userId: string;
    }

    // 扩展Response类型
    export interface Response {
      /** 200正常响应 */
      sendSuccess(data: unknown, message?: string): void;
      /** 非正常响应，错误的响应码需要自己传入 */
      sendError(message: string, code?: number): void;
    }
  }
}
